package com.projectframe.base.utils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

public class DataTransformUtil
{
    @SuppressWarnings("unchecked")
    public static <T> T transFormList(Class<?> clazz, Collection<?> list)
    {
        Collection<Object> newList = new ArrayList<Object>();
        
        for(Object obj : list)
        {
            newList.add(transForm(clazz, obj));
        }
     
        return (T)newList;
    }
    
    @SuppressWarnings("unchecked")
    public static <T> T transForm(Class<T> clazz, Object src)
    {
        Object target = null;
        
        try
        {
            target = clazz.newInstance();
        }
        catch (InstantiationException e1)
        {
            return null;
        }
        catch (IllegalAccessException e1)
        {
            return null;
        }
        
        List<Field> targetFields = new ArrayList<Field>();
        getFields(targetFields, clazz);
        
        for( Field tarField : targetFields )
        {
            Field srcField = null;
            Class<?> tarClazz = tarField.getType();

            try
            {
                srcField = getField(src.getClass(), tarField.getName());
                
                if(srcField!=null)
                {
                    Class<?> srcClzzz = srcField.getType();
                    
                    Object obj = srcField.get(src);
                    
                    if(tarClazz.isAssignableFrom(List.class) && srcClzzz.isAssignableFrom(List.class))
                    {
                        Type fc = tarField.getGenericType();
                        
                        if(fc==null)
                            continue;
                        
                        if(fc instanceof ParameterizedType)
                        {
                            ParameterizedType pt = (ParameterizedType)fc;
                            Class<?> genericClazz = (Class)pt.getActualTypeArguments()[0];
                            
                            List<?> list = transFormList(genericClazz, (List)obj);
                            tarField.set(target, list);
                        }
                    }
                    else if(tarClazz.isAssignableFrom(Map.class) || srcClzzz.isAssignableFrom(Map.class))
                    {
                        continue;
                    }
                    else
                    {
                        tarField.set(target, obj);
                    }
                }
            }
            catch (SecurityException e)
            {
                e.printStackTrace();
            }
            catch (IllegalAccessException e)
            {
                e.printStackTrace();
            }
            catch (IllegalArgumentException e)
            {
                e.printStackTrace();
                
                try
                {
                    Object item = transForm(tarField.getType(), srcField.get(src));
                    tarField.set(target, item);
                }
                catch (IllegalArgumentException e1)
                {
                    e1.printStackTrace();
                }
                catch (IllegalAccessException e1)
                {
                    e1.printStackTrace();
                }
            }
        }
        
        return (T)target;
    }
    
    private static void getFields(List<Field> list, Class<?> clazz)
    {
        Field field[] = clazz.getDeclaredFields();
        
        for( Field f : field )
        {
            f.setAccessible(true);
            list.add(f);
        }
        
        Class<?> superclass = clazz.getSuperclass();
        
        if( superclass!=null )
            getFields(list, superclass);
    }
    
    private static Field getField(Class<?> clazz, String fieldName)
    {
        Field field[] = clazz.getDeclaredFields();
        
        for( Field f : field )
        {
            if( f.getName().equals(fieldName) )
            {
                f.setAccessible(true);
                return f;
            }
        }
        
        Class<?> superclass = clazz.getSuperclass();
        
        if( superclass!=null )
            return getField(superclass, fieldName);
        
        return null;
    }
}
